#version 330 core

// Interpolated values from the vertex shaders
in vec2 UV;
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;
in vec3 secLightDirection_cameraspace;

// Ouput data
out vec3 color;

// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;
uniform vec3 secLightPosition_worldspace;

uniform vec3 ambient;
uniform vec3 diffuse;
uniform vec3 specular;

uniform int flag;

vec3 mod289(vec3 x)
{
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec2 mod289(vec2 x)
{
    return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x)
{
    return mod289(((x*34.0)+1.0)*x);
}

float snoise(vec2 v)
{
    const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0
                        0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
                        -0.577350269189626,  // -1.0 + 2.0 * C.x
                        0.024390243902439); // 1.0 / 41.0
// First corner
    vec2 i  = floor(v + dot(v, C.yy) );
    vec2 x0 = v -   i + dot(i, C.xx);

// Other corners
    vec2 i1;
    //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
    //i1.y = 1.0 - i1.x;
    i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    // x0 = x0 - 0.0 + 0.0 * C.xx ;
    // x1 = x0 - i1 + 1.0 * C.xx ;
    // x2 = x0 - 1.0 + 2.0 * C.xx ;
    vec4 x12 = x0.xyxy + C.xxzz;
    x12.xy -= i1;

// Permutations
    i = mod289(i); // Avoid truncation effects in permutation
    vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
                      + i.x + vec3(0.0, i1.x, 1.0 ));

    vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
    m = m*m ;
    m = m*m ;

// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;

// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
    m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );

// Compute final noise value at P
    vec3 g;
    g.x  = a0.x  * x0.x  + h.x  * x0.y;
    g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    return 130.0 * dot(m, g);
}

void main()
{

    // Light emission properties
    vec3 LightColor = vec3(1,1,1);
    float LightPower = 100.0f;

    vec3 secLightColor = vec3(1,1,1);
    float secLightPower = 100.0f;

    // Material properties
    vec3 MaterialDiffuseColor = diffuse;
    if((flag & 0x100) != 0){
        MaterialDiffuseColor = texture2D( myTextureSampler, UV ).rgb;
    }

    vec3 MaterialAmbientColor = ambient * MaterialDiffuseColor;
    vec3 MaterialSpecularColor = specular;

    if((flag & 0x1000) != 0){
        float  n = 0;
        vec2 v_texCoord = UV;

        vec2 uvw = v_texCoord + 0.1 * vec2( snoise(v_texCoord), snoise(v_texCoord) );

        n = snoise(uvw - vec2(0.0, 0.0));
        n += 0.5 * snoise(uvw * 2.0 - vec2(0.0, 0.0));
        n += 0.25 * snoise(uvw * 4.0 - vec2(0.0, 0.0));
        n += 0.125 * snoise(uvw * 8.0 - vec2(0.0, 0.0));
        n += 0.0625 * snoise(uvw * 16.0 - vec2(0.0, 0.0));
        n += 0.03125 * snoise(uvw * 32.0 - vec2(0.0, 0.0));
        n = n * 0.7;

         MaterialDiffuseColor += vec3(n, n, n);
    }


    vec3 fl = vec3(0, 0, 0);
    if((flag & 0x1) != 0){
        // Distance to the light
        float distance = length( LightPosition_worldspace - Position_worldspace );

        // Normal of the computed fragment, in camera space
        vec3 nc = normalize( Normal_cameraspace );
        // Direction of the light (from the fragment to the light)
        vec3 l = normalize( LightDirection_cameraspace );
        // Cosine of the angle between the normal and the light direction,
        // clamped above 0
        //  - light is at the vertical of the triangle -> 1
        //  - light is perpendicular to the triangle -> 0
        //  - light is behind the triangle -> 0
        float cosTheta = clamp( dot( nc,l ), 0,1 );

        // Eye vector (towards the camera)
        vec3 E = normalize(EyeDirection_cameraspace);
        // Direction in which the triangle reflects the light
        vec3 R = reflect(-l,nc);
        // Cosine of the angle between the Eye vector and the Reflect vector,
        // clamped to 0
        //  - Looking into the reflection -> 1
        //  - Looking elsewhere -> < 1
        float cosAlpha = clamp( dot( E,R ), 0,1 );

        fl =
            MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
            MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha, 20) / (distance*distance);
    }

    vec3 sl = vec3(0, 0, 0);
    if((flag & 0x10) != 0){
        // Distance to the light
        float distance = length( secLightPosition_worldspace - Position_worldspace );

        // Normal of the computed fragment, in camera space
        vec3 nc = normalize( Normal_cameraspace );
        // Direction of the light (from the fragment to the light)
        vec3 l = normalize( secLightDirection_cameraspace );
        // Cosine of the angle between the normal and the light direction,
        // clamped above 0
        //  - light is at the vertical of the triangle -> 1
        //  - light is perpendicular to the triangle -> 0
        //  - light is behind the triangle -> 0
        float cosTheta = clamp( dot( nc,l ), 0,1 );

        // Eye vector (towards the camera)
        vec3 E = normalize(EyeDirection_cameraspace);
        // Direction in which the triangle reflects the light
        vec3 R = reflect(-l,nc);
        // Cosine of the angle between the Eye vector and the Reflect vector,
        // clamped to 0
        //  - Looking into the reflection -> 1
        //  - Looking elsewhere -> < 1
        float cosAlpha = clamp( dot( E,R ), 0,1 );

        sl =
            MaterialDiffuseColor * secLightColor * secLightPower * cosTheta / (distance*distance) +
            MaterialSpecularColor * secLightColor * secLightPower * pow(cosAlpha, 20) / (distance*distance);
    }

    if( ((flag & 0x10) | (flag & 0x1)) != 0){
        color = MaterialAmbientColor + sl + fl;
    } else {
        color = MaterialDiffuseColor;
    }

}
